﻿@using Radzen
@using System.Collections
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.JSInterop
@using System.Linq.Dynamic.Core;
@using Radzen.Blazor.Rendering;
 
@typeparam TValue
@inherits FormComponent<TValue>
@if (Visible)
{
    <div @ref="Element" style="@Style" @attributes="Attributes" class="@GetCssClass()" id="@GetId()">
        @if (Range)
        {
            <span class="rz-slider-range" style="left: @((MinValue() / Max * 100).ToInvariantString())%; width: @(((MaxValue() / Max * 100) - (MinValue() / Max * 100)).ToInvariantString())%;"></span>
            <span @ref="minHandle" class="rz-slider-handle" style="left: @((MinValue() / Max * 100).ToInvariantString())%; bottom: auto;"></span>
            <span @ref="maxHandle"class="rz-slider-handle rz-slider-handle-active" style="left: @((MaxValue() / Max * 100).ToInvariantString())%; bottom: auto;"></span>
        }
        else
        {
            <span class="rz-slider-range rz-slider-range-min" style="width: @((MinValue() / Max * 100).ToInvariantString())%;"></span>
            <span @ref="handle" class="rz-slider-handle" style="left: @((MinValue() / Max * 100).ToInvariantString())%;"></span>
        }
    </div>
}
@code {
    ElementReference handle;
    ElementReference minHandle;
    ElementReference maxHandle;

    private bool visibleChanged = false;
    private bool disabledChanged = false;
    private bool maxChanged = false;
    private bool minChanged = false;
    private bool rangeChanged = false;
    private bool stepChanged = false;
    private bool firstRender = true;

    public override async Task SetParametersAsync(ParameterView parameters)
    {
        visibleChanged = parameters.DidParameterChange(nameof(Visible), Visible);
        disabledChanged = parameters.DidParameterChange(nameof(Disabled), Disabled);
        maxChanged = parameters.DidParameterChange(nameof(Max), Max);
        minChanged = parameters.DidParameterChange(nameof(Min), Min);
        rangeChanged = parameters.DidParameterChange(nameof(Range), Range);
        stepChanged = parameters.DidParameterChange(nameof(Step), Step);

        await base.SetParametersAsync(parameters);

        if ((visibleChanged || disabledChanged) && !firstRender)
        {
            if (Visible == false || Disabled == true)
            {
                Dispose();
            }
        }
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await base.OnAfterRenderAsync(firstRender);

        this.firstRender = firstRender;

        if (firstRender || visibleChanged || disabledChanged || maxChanged || minChanged || rangeChanged || stepChanged)
        {
            visibleChanged = false;
            disabledChanged = false;

            if (maxChanged)
            {
                maxChanged = false;
            }

            if (minChanged)
            {
                minChanged = false;
            }

            if (rangeChanged)
            {
                rangeChanged = false;
            }

            if (stepChanged)
            {
                stepChanged = false;
            }

            if (Visible && !Disabled)
            {
                await JSRuntime.InvokeVoidAsync("Radzen.createSlider", UniqueID, Reference, Element, Range, Range ? minHandle : handle, maxHandle, Min, Max, Value, Step);

                StateHasChanged();
            }
        }
    }

    public override void Dispose()
    {
        base.Dispose();

        if (IsJSRuntimeAvailable)
        {
            JSRuntime.InvokeVoidAsync("Radzen.destroySlider", UniqueID, Element);
        }
    }

    [JSInvokable("RadzenSlider.OnValueChange")]
    public async System.Threading.Tasks.Task OnValueChange(decimal value, bool isMin)
    {
        var step = string.IsNullOrEmpty(Step) || Step == "any" ? 1 : decimal.Parse(Step.Replace(",", "."), System.Globalization.CultureInfo.InvariantCulture);

        var newValue = Math.Round(value / step) * step;

        if (Range)
        {
            var oldMinValue = ((IEnumerable)Value).OfType<object>().FirstOrDefault();
            var oldMaxValue = ((IEnumerable)Value).OfType<object>().LastOrDefault();

            var type = typeof(TValue).IsGenericType ? typeof(TValue).GetGenericArguments()[0] : typeof(TValue);
            var convertedNewValue = ConvertType.ChangeType(newValue, type);

            var newValueAsDecimal = (decimal)ConvertType.ChangeType(newValue, typeof(decimal));
            var oldMaxValueAsDecimal = (decimal)ConvertType.ChangeType(oldMaxValue, typeof(decimal));
            var oldMinValueAsDecimal = (decimal)ConvertType.ChangeType(oldMinValue, typeof(decimal));

            var values = Enumerable.Range(0,2).Select(i => {
                if (i == 0)
                {
                    return isMin &&
                        !object.Equals(oldMinValue, convertedNewValue) &&
                        newValueAsDecimal >= Min && newValueAsDecimal <= Max &&
                        newValueAsDecimal < oldMaxValueAsDecimal
                            ? convertedNewValue : oldMinValue;
                }
                else
                {
                    return !isMin &&
                        !object.Equals(oldMaxValue, convertedNewValue) &&
                        newValueAsDecimal >= Min && newValueAsDecimal <= Max &&
                        newValueAsDecimal > oldMinValueAsDecimal
                            ? convertedNewValue : oldMaxValue;
                }
            }).AsQueryable().Cast(type);

            if (!object.Equals(Value, values))
            {
                Value = (TValue)values;

                await ValueChanged.InvokeAsync(Value);

                if (FieldIdentifier.FieldName != null)
                {
                    EditContext?.NotifyFieldChanged(FieldIdentifier);
                }

                await Change.InvokeAsync(Value);

                StateHasChanged();
            }
        }
        else
        {
            var valueAsDecimal = Value == null ? 0 : (decimal)ConvertType.ChangeType(Value, typeof(decimal));

            if (!object.Equals(valueAsDecimal, newValue) && newValue >= Min && newValue <= Max)
            {
                Value = (TValue)ConvertType.ChangeType(newValue, typeof(TValue));

                await ValueChanged.InvokeAsync(Value);

                if (FieldIdentifier.FieldName != null)
                {
                    EditContext?.NotifyFieldChanged(FieldIdentifier);
                }

                await Change.InvokeAsync(Value);

                StateHasChanged();
            }
        }
    }

    protected override string GetComponentCssClass()
    {
        return $"rz-slider {(Disabled ? "rz-state-disabled " : "")}rz-slider-horizontal";
    }

    [Parameter]
    public override TValue Value
    {
        get
        {
            if (_value == null)
            {
                if (Range)
                {
                    var type = typeof(TValue).IsGenericType ? typeof(TValue).GetGenericArguments()[0] : typeof(TValue);

                    _value = (TValue)Enumerable.Range(0, 2).Select(i =>
                    {
                        if (i == 0)
                        {
                            return ConvertType.ChangeType(Min, type);
                        }
                        else
                        {
                            return ConvertType.ChangeType(Max, type);
                        }
                    }).AsQueryable().Cast(type);
                }
                else
                {
                    _value = (TValue)ConvertType.ChangeType(Min, typeof(TValue));
                }
            }

            return _value;
        }
        set
        {
            if (!EqualityComparer<TValue>.Default.Equals(value, _value))
            {
                _value = value;
            }
        }
    }

    public override bool HasValue
    {
        get
        {
            return Value != null;
        }
    }

    decimal MinValue()
    {
        if (Range)
        {
            var values = Value as IEnumerable;
            if (values != null && values.OfType<object>().Any())
            {
                var v = values.OfType<object>().FirstOrDefault();
                return (decimal)Convert.ChangeType(v != null ? v : Min, typeof(decimal));
            }
        }

        return HasValue ? (decimal)Convert.ChangeType(Value, typeof(decimal)) : Min;
    }

    decimal MaxValue()
    {
        if (Range)
        {
            var values = Value as IEnumerable;
            if (values != null && values.OfType<object>().Any())
            {
                var v = values.OfType<object>().LastOrDefault();
                return (decimal)Convert.ChangeType(v != null ? v : Max, typeof(decimal));
            }
        }

        return HasValue ? (decimal)Convert.ChangeType(Value, typeof(decimal)) : Min;
    }

    [Parameter]
    public string Step { get; set; } = "1";

    [Parameter]
    public bool Range { get; set; } = false;

    [Parameter]
    public decimal Min { get; set; } = 0;

    [Parameter]
    public decimal Max { get; set; } = 100;
}